#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "pear_block.h"
#include "utils/pear_dump.h"
#include "transaction/pear_tx_default.h"


#define CREATE_TABLE_STRING "

/*
 * Transaction database:
 * ID | hash | from | to | amount | gas | fee | signature | timestamp 
 *
 * Block database:
 * Numner | Timestamp | TxCounter | GasUsed | Coinbase | PrevBlock | MerkleRoot | Nonce
 *
 */
const char Create_Transactions_Table[] = "     \
    CREATE TABLE IF NOT EXISTS Transactions (  \
        ID         BIGINT PRIMARY KEY,         \
        BLOCKID    BIGINT,                     \
        Hash       CHARACTER(32),              \
        From       CHARACTER(20),              \
        To         CHARACTER(20),              \
        Amount     BIGINT UNSIGNED,            \
        Gas        BIGINT UNSIGNED,            \
        Fee        BIGINT UNSIGNED,            \
        Code       VARCHAR(2048),              \
        Signature  CHARACTER(72),              \
        Timestamp  INT UNSIGNED                \
    )";

const char Insert_Transaction_Table[] = "      \
    INSERT INTO Transactions (                 \
        BLOCKID, Hash, From, To, Amount,       \
        Gas, Fee, Code, Signature, Timestamp)  \
    VALUES(%ld, %s, %s, %s, %s, %d, %d, %s, %s, %d);

const char Create_Block_Table[] = "            \
    CREATE TABLE IF NOT EXISTS Block (         \
        Number     BIGINT PRIMARY KEY,         \
        Timestamp  INT    UNSIGNED             \
        TxCounter  BIGINT UNSIGNED,            \
        GasUsed    BIGINT UNSIGNED,            \
        Coinbase   CHARACTER(20),              \
        PrevBlock  CHARACTER(32),              \
        MerkleRoot CHARACTER(32),              \
        Nonce      BIGINT UNSIGNED             \
    )";

const char Insert_Block_Table[] = "            \
    INSERT TABLE Block (                       \
        Number, Timestamp, TxCounter, GasUsed, \
        Coinbase, PrevBlock, MerkleRoot, Nonce \
    ) VALUES(%ld, %d, %d, %d, %s, %s, %s, %d)";


/*
    "CREATE INDEX IF NOT EXISTS TxHashIdx ON   \
        Transactions(Hash);",

    "CREATE INDEX IF NOT EXISTS BlkHashIdx ON  \
       Block(Hash);",
*/

typedef struct pr_blk_store_s pr_blk_store_t;

struct pr_blk_store_s {
    sqlite3 *db;
    char     prefix[64];
}


pr_blk_store_t *pr_new_block_store(char *prefix)
{
    pr_blk_store_t *blk_store = NULL;

    blk_store = (pr_blk_store_t *) malloc(sizeof(pr_blk_sotre_t));
    if (blk_store == NULL)
    {
        PEAR_LOG("malloc pr_blk_sotre_t error\n")
        return NULL;
    }

    strcpy(blk_store->prefix, prefix);

    /* Open database */
    rc = sqlite3_open(blk_store->prefix, &blk_store->db);
    if (rc)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(blk_store->db));
        free(blk_store);
        return NULL;
    }
    else
    {
        fprintf(stdout, "Opened database successfully\n");
    }

    if (pr_create_tx_table(blk_store) < 0)
    {
        pr_blk_store_free(blk_store);
        return -1;
    }

    if (pr_create_blk_table(blk_store) < 0)
    {
        pr_blk_store_free(blk_store);
        return -1;
    }

    return blk_store;
}

void pr_blk_store_free(pr_blk_store_t *blk_store)
{
    if (blk_store != NULL)
    {
        if (blk_store->db != NULL)
        {
            sqlite3_close(blk_store->db);
        }

        free(blk_sotre);
    }
}

static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
    int i;

    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");

    return 0;
}

int pr_create_tx_table(pr_blk_store_t *blk_store)
{
    char *zErrMsg = 0;
    /* Execute SQL statement */
    rc = sqlite3_exec(blk_store->db, Create_Transactions_Table, callback, 0, &zErrMsg);

    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return -1;
    }
    else
    {
        fprintf(stdout, "Table created successfully\n");
    }

    return 1;
}

int pr_create_blk_table()
{
    char *zErrMsg = 0;
    /* Execute SQL statement */
    rc = sqlite3_exec(blk_store->db, Create_Block_Table, callback, 0, &zErrMsg);

    if ( rc != SQLITE_OK )
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return -1;
    }
    else
    {
        fprintf(stdout, "Table created successfully\n");
    }

    return 1;
}


int pr_store_block(pr_block_header_t *blk)
{
    /* Execute SQL statement */
    char *zErrMsg = 0;
    char  sql[1024];

    sprintf(sql,
            Insert_Block_Table,
            blk->number,
            blk->timestamp,
            blk->tx_counter,
            blk->total_gas,
            pr_hex_encode(temp_buf, blk->coinbase, sizeof(blk->coinbase)),
            pr_hex_encode(temp_buf, blk->prev_block, sizeof(blk->prev_block)),
            blk->merkle_root,
            blk->nonce);

    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return -1;
    }
    else
    {
        fprintf(stdout, "Records created successfully\n");
    }

    return 1;
}

int pr_store_tx(pr_tx_t *tx, long long int blockId, char *hash, int timestamp)
{
    /* Execute SQL statement */
    char  temp_buf[1024];
    char *zErrMsg = 0;
    char  sql[1024];
    sprintf(sql,
            Insert_Transactions_Table,
            blockId,
            hash,
            pr_hex_decode(temp_buf, tx->input->from, sizeof(tx->input->from)),
            pr_hex_decode(temp_buf, tx->output->to, sizeof(tx->output->to)),
            tx->output->amount,
            tx->gas,
            tx->fee,
            "",
            pr_hex_decode(temp_buf, tx->input->sign, sizeof(tx->input->sign)),
            timestamp);

    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return -1;
    }
    else
    {
        fprintf(stdout, "Records created successfully\n");
    }

    return 1;
}



